import inspect
import traceback
import mui
import mui.InternalUse.clock as clock

EVENT_HANDLED = True 
EVENT_UNHANDLED = None

    
class EventException(Exception):
    '''An exception raised when an event handler could not be attached.
    '''
    pass

class MUIEventHandler(object):
    def __init__(self, func, funcargs, funckwargs, **kwargs):
        self.noarg = kwargs.get('noarg', False)
        self.func = func
        self.bPostEvent = kwargs.get('postevent', False)
        self.args = funcargs
        self.kwargs = funckwargs

    def call(self, *args, **kwargs):
        if (self.noarg):
            act = clock.Action(self.func, self.args, self.kwargs, bPostCall = self.bPostEvent)
        else:
            act = clock.Action(self.func, args, kwargs, bPostCall = self.bPostEvent)
        act.call()



class EventDispatcher(object):
    '''Generic event dispatcher interface.

    See the module docstring for usage.
    '''
    # Placeholder empty stack; real stack is created only if needed


    def __init__(self):
        self._event_stack = ()

    def __del__(self):
        pass

    def clear(self):
        self._event_stack = ()

    def append_handler(self, name, func, *args, **kwargs):
        bPostEvent = kwargs.get('postevent', False)
        bNoarg = kwargs.get('noarg', False)
        handler = MUIEventHandler(func, args, {}, postevent = bPostEvent, noarg = bNoarg)

        if type(self._event_stack) is tuple:
            self._event_stack = [{}]
        if not self._event_stack[0].has_key(name):
            self._event_stack[0][name] = []
        self._event_stack[0][name].append(handler)

    def remove_handler(self, name, handler):
        for frame in self._event_stack:
            try:
                if (isinstance(handler, MUIEventHandler)):
                    if handler in range(0, len(frame[name])):
                        frame[name].remove(handler)
                        break
                else: #handler is a function
                    isFound = False
                    for i in range(0, len(frame[name])):
                        if frame[name][i].func == handler:
                            frame[name].pop(i)
                            isFound = True
                            break
                    if (isFound):
                        break
            except KeyError:
                pass

    def dispatch_event(self, event_type, *args):
        '''Dispatch a single event to the attached handlers.
        
        The event is propagated to all handlers from from the top of the stack
        until one returns `EVENT_HANDLED`.  This method should be used only by
        `EventDispatcher` implementors; applications should call
        the ``dispatch_events`` method.

        :Parameters:
            `event_type` : str
                Name of the event.
            `args` : sequence
                Arguments to pass to the event handler.

        '''
        #assert event_type in self.event_types        #event_types is used for push_handlers to decide which methods need to be bind, we don't need it here
        ret = False
        # Search handler stack for matching event handlers
        for frame in self._event_stack:
            handlers = frame.get(event_type, None)
            if handlers:
                ret = True
                for handler in handlers:
                    try:
                        handler.call(*args)

                    except TypeError:
                        self._raise_dispatch_exception(event_type, args, handler)

        return ret


